#include<iostream>
#include<stdio.h>
#include<stdlib.h>
#include<string>
#include<string.h>
#include<arpa/inet.h>
#include<unistd.h>
#include<sys/types.h>
#include<errno.h>
#include<fcntl.h>
#include<sys/epoll.h>
#include"locker.h"
#include"thread_pool.hpp"
#include<libgen.h>
#include<signal.h>
#include"http_connect.h"
#include"epoll_add_del.h"
#include"lst_timer.h"
using namespace std;



//主线程维持一个定时器链表
static sort_time_lst* lst = new sort_time_lst;

static int timefd[2];

/*
同步IO模拟实现proactor模式
*/
#define TIMESLOT 5
#define MAX_FD 65535 //最大的文件描述符个数
#define MAX_EVENT_NUMBER 10000 //监听的最大的事件数量

void addsig(int sig,void(handler)(int)){
   struct sigaction sa;
   sa.sa_handler = handler;
   sigfillset(&sa.sa_mask);
   sigaction(sig,&sa,NULL);
}

//添加文件描述符到epoll中
extern void addfd(int epollfd,int fd, bool one_shot);
//从epoll中删除文件描述符
extern void removefd(int epollfd,int fd);
//从epoll中修改文件描述符
extern void modfd(int epollfd,int fd,int env);

//捕捉到SIGALRM或SIGTERM后执行的回调函数，往timefd写数据
void sig_handler(int sig){
    int save_errno = errno;
    int msg = sig;
    send( timefd[1], ( char* )&msg, 1, 0 );
    errno = save_errno;
}

//每个定时器需要执行的操作
void cb_func(http_connect* user_data){
    epoll_ctl(user_data->m_epollfd,EPOLL_CTL_DEL,user_data->m_sockfd,NULL);
    close(user_data->m_sockfd);
}

void timer_handler(){
    lst->tick();
    alarm(TIMESLOT);
}


//运行程序，指定一下端口号
int main(int argc,char* argv[]){

    //定义捕捉SIGALRM
    // struct sigaction funcALRM;
    // memset(&funcALRM,'\0',sizeof(funcALRM));
    // sigfillset(&funcALRM.sa_mask);
    // funcALRM.sa_flags = 0;
    // funcALRM.sa_handler = sig_handler;
    // sigaction(SIGALRM,&funcALRM,NULL);
    addsig(SIGALRM,sig_handler);

    //定义捕捉SIGTERM（程序结束信号）
    // struct sigaction funcTERM;
    // memset(&funcTERM,'\0',sizeof(funcTERM));
    // sigfillset(&funcTERM.sa_mask);
    // funcTERM.sa_flags = 0;
    // funcTERM.sa_handler = sig_handler;
    // sigaction(SIGTERM,&funcTERM,NULL);
    addsig(SIGTERM,sig_handler);

    if(argc<=1){
        cout<<"按照如下格式运行："<<static_cast<string>(basename(argv[0]))<<" port_number"<<endl;
    }

    //读取端口号
    int port = atoi(argv[1]);

    //对SIGPIPE限号进行处理 
    addsig(SIGPIPE,SIG_IGN);

    //创建线程池，初始化线程池
    //任务是连接形任务
    threadpool<http_connect>* pool  = NULL;
    try{
        pool = new threadpool<http_connect>;
    }
    catch(...){
        exit(-1);
    }

    //创建一个数组用于保存所有的客户端信息
    http_connect* users = new http_connect[MAX_FD];

    int listenfd = socket(PF_INET,SOCK_STREAM,0);
    if(listenfd==-1){
        perror("socket listen");
        exit(-1);
    }

     //设置端口复用
     int optval = 1;
     if(setsockopt(listenfd,SOL_SOCKET,SO_REUSEPORT,&optval,sizeof(optval))==-1){
         perror("setsockopt");
         exit(-1);
     }


     //绑定
     struct sockaddr_in address;
     address.sin_family = AF_INET;
     address.sin_port = htons(port);
     address.sin_addr.s_addr = INADDR_ANY;
     if(bind(listenfd,(struct sockaddr*)&address,sizeof(address))==-1){
         perror("bind");
         exit(-1);
     }

     //监听
     if(listen(listenfd,5)==-1){
         perror("listen");
         exit(-1);
     }

     //创建epoll对象，事件数组的添加
    struct epoll_event events[MAX_EVENT_NUMBER]; //存储后面返回的修改事件
    int epollfd = epoll_create(5);

    //将监听的文件描述符添加到epoll
    addfd(epollfd,listenfd,false);
    http_connect::m_epollfd = epollfd;


    /*
    创建定时器到时监听
    1.创建一个读写管道
    2.设置信号回调函数为往管道写数据（写入收到的信号）
    3.将管道读端监听，如 第2步写入了，则可以捕捉到，则可以对定时器链表进行操作（删除一些超时的）
    注：有一个点需要特别注意，你捕捉到了SIGALRM信号，你是应该立马处理超时的，还是将现在的读完才去处理呢？
    我的理解是读完现在的优先级更高，所以可以设置一个output标记读完全部的要去看看定时器链表了
    
    */
   
   if (socketpair(AF_UNIX,SOCK_STREAM,0,timefd)<0){
       perror("socketpair");
       exit(-1);
   }
    //写端设置非阻塞，监听读端
    setnonblocking(timefd[1]);
    addfd(epollfd,timefd[0],false);

    
    alarm(TIMESLOT); //这里之所以不用setitime,是因为要等处理后，再定时
    
    bool output = false; //此时是否到时，需要对链表进行检查
    bool stop_server = false;
    
    while(!stop_server){
        
       int num =  epoll_wait(epollfd,events,MAX_EVENT_NUMBER,-1);
       
        if(num<0 && errno!=EAGAIN&&errno!=EINTR){
            cout<<"epoll failure"<<endl;
            break;
        }
        //循环遍历事件数组
        // cout<<"num"<<num<<endl;
        for(int i = 0;i<num;++i){
            int sockfd = events[i].data.fd;
            if(sockfd==listenfd){
                //有客户端连接进来
                sockaddr_in client_address;
                socklen_t client_addrlen = sizeof(client_address);
                int connfd = accept(listenfd,(struct sockaddr*)&client_address,&client_addrlen);
                if(connfd==-1){
                    perror("accept");
                    exit(-1);
                }

                if(http_connect::m_user_count>=MAX_FD){
                    //目前连接已经满了
                    //给客户端回送一个信息：服务器正忙。。

                    /*
                    //要封装成http报文才可以发送
                    char* buf = "The server is busy, please connect later";
                    send(connfd,buf,strlen(buf)+1,0);
                    
                    
                    */
                    close(connfd);
                    continue;
                }

                // 将新的客户的数据初始化，放到user数组中，且添加到epoll内核
                util_timer* timer = new util_timer;
                timer->cb_func = cb_func;
                time_t cur = time(NULL);
                timer->expire = cur*3*TIMESLOT;
                // timer->user_data = 
                timer->user_data = users+connfd;

                users[connfd].init(connfd,client_address,timer);
                lst->add_timer(timer);
            }
            else if(events[i].events&(EPOLLRDHUP|EPOLLHUP|EPOLLERR)){
                 //对方异常断开或者错误等事件
                 users[sockfd].close_connection();

            }
            else if(events[i].events&EPOLLIN){
               
                if(sockfd == timefd[0]){
                    //信号处理
                    int sig;
                    char signal[1024]={0};
                    int ret = recv(sockfd,signal,sizeof(signal),0);
                    //  cout<<"ret"<<ret<<endl;
                    if(ret==-1){
                        continue;
                    }
                    else if(ret==0){
                        continue;
                    }
                    else {
                        for(int j = 0;j<ret;++j){
                            switch(signal[j]){
                                case SIGALRM:{
                                     
                                    output = true; //需要检查链表了
                                    break;
                                }
                                case SIGTERM:{
                                    stop_server = true; //需要终止服务器程序了
                                    // break;
                                }

                            }
                        }
                    }
                }
                else{
                    //根据proactor模式，要把数据全部读下来
                
                    if(users[sockfd].read()){
                        //一次把数据全部读完
                        pool->append(users+sockfd);
                        //这个sockfd的定时器得重新更新
                        time_t cur = time(NULL);
                        users[sockfd].http_time->expire = cur+3*TIMESLOT; 
                        lst->adjust_timer(users[sockfd].http_time);
                    }
                    else{
                        users[sockfd].close_connection();
                        lst->del_timer(users[sockfd].http_time);  //也删除链表中的定时器
                    }
                }
            }
            else if(events[i].events&EPOLLOUT){
                //一次性写完全部的数据
                if(!users[sockfd].write()){
                    //写失败了或者不需要保持连接了
                    users[sockfd].close_connection();
                }
            }


        }
        if(output){
            // cout<<"到这了没"<<endl;
            timer_handler();
            output = false;
        }
    }
    close(epollfd);
    close(listenfd);
    delete [] users;
    delete pool;




    return 0;
}